import config
import environ
from environ import *
import random as random

class Agent(object):
    SEARCHING_FOR_AGENT=1
    AGENT_FOUND=2

    def __init__(self, ID, env, coord=None):
        assert isinstance(env, environ.Environment)
        self.ID=ID
        self.env=env
        self._map=AgentMap(Config.MAPDIMENSIONS)
        if coord==None:
            x=random.randint(0, env.envmap.width-1)
            y=random.randint(0, env.envmap.height-1)
            self.coord=(x,y)
        else:
            self.coord=coord
        self._map.set_tile(self.coord, Tile(Tile.NORMAL))
        self.known_next_coords = {}
        self.prev_nearby_agents=set([])
        
    def next_turn(self):
        nearby_agents = self.env.sense_nearby_agents(self)
        agents = []
        for agent, dst in nearby_agents:
            agents.append(agent.ID)
        
        self.known_next_coords.clear()
        message = self.env.receive_message(self.ID)
        while message:
            source_id, msg_type, msg = message
            if msg_type == 'tile':
                coord, tile = msg
                self._map.set_tile(coord, Tile(tile))
            elif msg_type == 'next_coord':
                self.known_next_coords[source_id] = msg
            else:
                raise Exception("Unknown message type %s" % msg_type)
            message = self.env.receive_message(self.ID)
            
        for id, coord in self.known_next_coords.iteritems():
            if id not in agents:
                self._map.set_tile(coord, Tile(Tile.MINE))
            
        self._map.set_tile(self.coord, Tile(Tile.NORMAL))
        direction = self.decide_direction(nearby_agents)
        
        next_coord = self._map.get_next_tile(self.coord, direction)
        for agent, dst in nearby_agents:
            if dst <= Config.COMMUNICATION_RANGE - 3:
                self.env.send_message(self.ID, agent.ID, 'next_coord', next_coord)
            for coord, tile in self._map.get_known_tiles():
                self.env.send_message(self.ID, agent.ID, 'tile', (coord, tile))
        self._move(direction);
            
    def decide_direction(self, nearby_agents):
        directions = range(0, 8)
        
        if nearby_agents:
            for agent, dst in nearby_agents:
                if dst > Config.COMMUNICATION_RANGE / 2:
                    d = self._map.get_direction(self.coord, agent.coord)
                    directions.extend([d, d])
                else:
                    d = self._map.get_direction(self.coord, agent.coord)
                    # calculate the opposite direction
                    d = Direction().opposite(d)
                    directions.extend([d, d, d, d, d])

        for d in xrange(0, 8):
            coord = self._map.get_next_tile(self.coord, d)
            if self._map.in_map(coord) and Tile.UNMAPPED == self._map.get_tile(coord).tiletype:
                directions.extend([d, d, d, d])
        
        prohibited_directions = []
        for direction in xrange(0, 8):
            coord = self._map.get_next_tile(self.coord, direction)
            if not self._map.in_map(coord) or self._map.get_tile(coord).tiletype == Tile.MINE:
                prohibited_directions.append(direction)
        
        for direction in directions[:]:
            if direction in prohibited_directions:
                directions.remove(direction)
        
        return random.choice(directions)    
    
    def _move(self, direction):
        self.env.move(self, direction)            

        
class AgentMap(Map):
    def __init__(self, dimensions):
        self.width, self.height=dimensions
        self._map=[]
        for col in xrange(0, self.width):
            col=[]
            for tile in xrange(0, self.height):
                col.append(Tile(Tile.UNMAPPED))
            self._map.append(col)
            
    def get_known_tiles(self):
        ret = []
        for x, col in enumerate(self._map):
            for y, tile in enumerate(col):
                if tile.tiletype == Tile.MINE:
                    ret.append(((x, y), tile.tiletype))
                    # we don't want to send too many messages
                    if len(ret) == Config.MAX_NUMBER_OF_MESSAGES - 1:
                        return ret

        for x, col in enumerate(self._map):
            for y, tile in enumerate(col):
                if tile.tiletype == Tile.NORMAL:
                    ret.append(((x, y), tile.tiletype))
                    # we don't want to send too many messages
                    if len(ret) == Config.MAX_NUMBER_OF_MESSAGES - 1:
                        return ret

        return ret


# it's a pure module:		
if __name__ == "__main__":
    pass
